package com.path.planning.util.path;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.deductive.common.exception.ServiceException;
import com.path.planning.entity.path.*;
import com.path.planning.mapper.path.NavigationMapper;
import com.path.planning.util.path.entity.*;
import org.jgrapht.Graph;
import org.jgrapht.GraphPath;
import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
import org.jgrapht.graph.WeightedPseudograph;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.MultiLineString;
import org.locationtech.jts.geom.Point;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
 * 路径规划工具类
 */
//@Component
public class NavigationUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(NavigationUtils.class);
    /**
     * 路径规划线程池（参数化，防止资源耗尽）
     */
    private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors();
    private static final int MAX_POOL_SIZE = CORE_POOL_SIZE * 2;
    private static final int QUEUE_CAPACITY = 1000;
    private static final long KEEP_ALIVE_TIME = 60L;
    public static final ExecutorService NAVIGATION_EXECUTOR = new java.util.concurrent.ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE_TIME, java.util.concurrent.TimeUnit.SECONDS,
            new java.util.concurrent.LinkedBlockingQueue<>(QUEUE_CAPACITY),
            new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy()
    );

    // 魔法数字常量
    private static final double DEFAULT_PENALTY = 1.0;
    private static final double WATERWAY_COST_FACTOR = 0.1;
    private static final int MAX_AVOID_DISTANCE = 5000;

    @Resource
    private NavigationMapper navigationMapper;

    /**
     * 路径规划
     *
     * @param navigationQo 条件参数 {@link NavigationQo}
     */
    public static NavigationResVo doNavigation(NavigationQo navigationQo, NavigationConstants.NavigationTypeEnum navigationType) {
        Graph<Integer, CustomEdge> graph;
        switch (navigationType) {
            case ROAD_TIME:
                graph = buildGraphWithLimit(navigationQo, ROAD_TIME_GRAPH);
                break;
            case ROAD_LENGTH:
                graph = buildGraphWithLimit(navigationQo, ROAD_LENGTH_GRAPH);
                break;
            case ROAD_HIGHWAY:
                graph = buildGraphWithLimit(navigationQo, ROAD_HIGHWAY_GRAPH);
                break;
//            case RAILWAY:
//                graph = buildGraphWithLimit(navigationQo, RAILWAY_GRAPH);
//                break;
//            case WATERWAY:
//                graph = buildGraphWithLimit(navigationQo, WATERWAY_GRAPH);
//                break;
//            case ROAD_RAILWAY:
//                graph = buildGraphWithLimit(navigationQo, ROAD_RAILWAY_MIXED_GRAPH);
//                break;
//            case ROAD_WATERWAY:
//                graph = buildGraphWithLimit(navigationQo, ROAD_WATERWAY_MIXED_GRAPH);
//                break;
            default:
                throw new ServiceException("规划类型非法, 无法规划!");
        }
        // 构建铁路有权无向图
        DijkstraShortestPath<Integer, CustomEdge> shortestPath = new DijkstraShortestPath<>(graph);
        // 起点 和终点的 临近拓扑点ID
        if (null == navigationQo.getSourceId() || null == navigationQo.getTargetId()) {
            throw new ServiceException("起点或终点对应拓扑点为空");
        }
        // 查询最短路径
        GraphPath<Integer, CustomEdge> shortestPathInfo = shortestPath.getPath(navigationQo.getSourceId(), navigationQo.getTargetId());
        if (CollUtil.isEmpty(shortestPathInfo.getEdgeList())) {
            throw new ServiceException("无路径规划");
        }
        return unionEdgeAndSetPartEdgeInfo(navigationQo.getSourceId(), shortestPathInfo.getEdgeList(), navigationType, graph);
    }

    /**
     * 构建带过滤条件的权重拓扑图
     *
     * @param navigationQo  过滤条件 {@link NavigationQo}
     * @param originalGraph 原始路网拓扑图数据 {@link WeightedPseudograph}
     * @return 带过滤条件的权重拓扑图 {@link CustomGraphWrapper}
     */
    private static CustomGraphWrapper<Integer, CustomEdge> buildGraphWithLimit(NavigationQo navigationQo, WeightedPseudograph<Integer, CustomEdge> originalGraph) {
        List<Geometry> avoidGeometryList = buildAvoidGeometry(navigationQo.getAvoidScopes());
        Set<CustomEdge> edgesToAvoid = filterEdgesByLimit(navigationQo, originalGraph, avoidGeometryList);
        return new CustomGraphWrapper<>(originalGraph, edgesToAvoid);
    }

    /**
     * 根据限高、限宽、限重、避让区等条件过滤边
     */
    private static Set<CustomEdge> filterEdgesByLimit(NavigationQo navigationQo, WeightedPseudograph<Integer, CustomEdge> originalGraph, List<Geometry> avoidGeometryList) {
        Set<CustomEdge> edgesToAvoid = Collections.synchronizedSet(new HashSet<>());
        originalGraph.edgeSet().stream().parallel().forEach(edge -> {
            PathTopologyData path = edge.getPathTopologyData();
            try {
                // 限高筛选
                if (navigationQo.getHeight() != null && navigationQo.getHeight() != 0.0 && path.getHeightLimit() != null && path.getHeightLimit() != 0.0 && path.getHeightLimit() < navigationQo.getHeight()) {
                    edgesToAvoid.add(edge);
                }
                // 限宽筛选
                if (navigationQo.getWidth() != null && navigationQo.getWidth() != 0.0 && path.getWidthLimit() != null && path.getWidthLimit() != 0.0 && path.getWidthLimit() < navigationQo.getWidth()) {
                    edgesToAvoid.add(edge);
                }
                // 超重筛选
                if (navigationQo.getWeight() != null && navigationQo.getWeight() != 0.0 && path.getWeightLimit() != null && path.getWeightLimit() != 0.0 && path.getWeightLimit() < navigationQo.getWeight()) {
                    edgesToAvoid.add(edge);
                }
                // 涉水深度
                if (navigationQo.getDepth() != null && navigationQo.getDepth() != 0.0 && path.getDepthLimit() != null && path.getDepthLimit() != 0.0 && path.getDepthLimit() < navigationQo.getDepth()) {
                    edgesToAvoid.add(edge);
                }
                // 避让区
                if (CollUtil.isNotEmpty(avoidGeometryList)) {
                    for (Geometry geometry : avoidGeometryList) {
                        if (GeoUtils.isIntersects(geometry, path.getMulLineGeometry())) {
                            edgesToAvoid.add(edge);
                            break;
                        }
                    }
                }
            } catch (Exception e) {
                LOGGER.error("边过滤异常，edgeId:{}", edge.getEdgeId(), e);
            }
        });
        return edgesToAvoid;
    }

    /**
     * 构建避让几何对象
     *
     * @param avoidScopes 避让几何对象点位信息
     * @return 几何对象 {@link Geometry}
     */
    private static List<Geometry> buildAvoidGeometry(List<AvoidScopeDto> avoidScopes) {
        // 根据避让条件，构建避让geometry
        List<Geometry> avoidGeometryList = new ArrayList<>();
        if (CollUtil.isNotEmpty(avoidScopes)) {
            for (AvoidScopeDto avoidanceScope : avoidScopes) {
                if ("point".equals(avoidanceScope.getType())) {
                    Point point = GeoUtils.buildPointFromCoordinates(avoidanceScope.getCoordinates());
                    Geometry circle = point.buffer(metersToDegrees(avoidanceScope.getRadius(), point.getY()));
                    avoidGeometryList.add(circle);
                } else if ("line".equals(avoidanceScope.getType())) {
                    avoidGeometryList.add(GeoUtils.buildMultiLineStringFromCoordinates(avoidanceScope.getCoordinates()));
                } else if ("polygon".equals(avoidanceScope.getType())) {
                    avoidGeometryList.add(GeoUtils.buildPolygonFromCoordinates(avoidanceScope.getCoordinates()));
                }
            }
        }
        return avoidGeometryList;
    }

    /**
     * 处理路径信息(合并同名、设置路线是否逆向)
     *
     * @param navigationSourceId 导航起点ID
     * @param edgeList           分段路集合
     * @param navigationType     规划类型
     * @return 结果信息 {@link NavigationResVo}
     */
    private static NavigationResVo unionEdgeAndSetPartEdgeInfo(int navigationSourceId, List<CustomEdge> edgeList, NavigationConstants.NavigationTypeEnum navigationType, Graph<Integer, CustomEdge> originalGraph) {
        NavigationResVo res = new NavigationResVo();
        List<LineString> lineStrings = new ArrayList<>();
        Double totalTime = edgeList.stream().mapToDouble(edge -> edge.getPathTopologyData().getRoadTime()).sum();
        Double totalCost = edgeList.stream().mapToDouble(edge -> edge.getPathTopologyData().getCost()).sum();
        // 整理分段路径信息, 确认方向(由于规划时默认路径是无向的)
        List<NavigationDetailInfoVo> detailInfoList = new ArrayList<>();
        Map<String, Double> typePathLengthMap = new HashMap<>();
        int preTargetId = navigationSourceId; // 上一段路径的节点ID,用于判断 下一段路径判断方向时,是否需要逆向.
        for (CustomEdge edge : edgeList) {
            PathTopologyData pathTopologyData = edge.getPathTopologyData();
            NavigationDetailInfoVo preInfo = CollUtil.isEmpty(detailInfoList) ? null : detailInfoList.get(detailInfoList.size() - 1);
            if (preTargetId == originalGraph.getEdgeSource(edge)) {
                lineStrings.add(pathTopologyData.getLineGeometry());
                NavigationDetailInfoVo curInfo = new NavigationDetailInfoVo(edge, false);
                if (null != preInfo && canUnionPathInfo(preInfo, curInfo)) {
                    detailInfoList.set(detailInfoList.size() - 1, unionSamePath(preInfo, curInfo));
                } else {
                    detailInfoList.add(curInfo);
                }
                preTargetId = originalGraph.getEdgeTarget(edge);
            } else { // 逆向
                lineStrings.add(pathTopologyData.getReverseLineGeometry());
                NavigationDetailInfoVo curInfo = new NavigationDetailInfoVo(edge, true);
                if (null != preInfo && canUnionPathInfo(preInfo, curInfo)) {
                    detailInfoList.set(detailInfoList.size() - 1, unionSamePath(preInfo, curInfo));
                } else {
                    detailInfoList.add(curInfo);
                }
                preTargetId = originalGraph.getEdgeSource(edge);
            }
            // 加上这段路径长度
            calculatePathLength(edge, typePathLengthMap);
        }
        // 合并整线
        LineString lineString;
        if (navigationType.equals(NavigationConstants.NavigationTypeEnum.ROAD_RAILWAY)
                || navigationType.equals(NavigationConstants.NavigationTypeEnum.ROAD_WATERWAY)) {
            lineString = handleMixDisconnect(lineStrings);

        } else {
            lineString = GeoUtils.unionLineString(lineStrings);
        }
        if (null == lineString) {
            throw new ServiceException("路径规划内部异常");
        }
        res.setAbstractInfo(new NavigationAbstractInfoVo(navigationType.getName(), totalCost, totalTime, JSONObject.parseObject(GeoUtils.getGeoJsonStrFromGeometry(lineString))));
        res.setDetailInfoList(detailInfoList);
        res.setTypePathLength(typePathLengthMap);
        return res;
    }

    /**
     * 判断两段路径信息是否可以合并(根据名称)
     *
     * @param preInfo 上一段路径信息
     * @param curInfo 当前路径信息
     * @return true 合并；false 不合并
     */
    private static boolean canUnionPathInfo(NavigationDetailInfoVo preInfo, NavigationDetailInfoVo curInfo) {
        boolean canUnion = false;
        if (null == preInfo.getPathType()) {
            LOGGER.error(JSONObject.toJSONString(preInfo));
        }
        if (null == curInfo.getPathType()) {
            LOGGER.error(JSONObject.toJSONString(curInfo));
        }
        if (preInfo.getPathType().equals(curInfo.getPathType())) {
            if (StrUtil.isNotBlank(preInfo.getPathName()) && StrUtil.isNotBlank(curInfo.getPathName())) {
                if (preInfo.getPathName().trim().equals(curInfo.getPathName().trim())) {
                    canUnion = true;
                } else if (preInfo.getPathName().startsWith(curInfo.getPathName()) || curInfo.getPathName().startsWith(preInfo.getPathName())) {
                    canUnion = true;
                }
            }
        }
        return canUnion;
    }

    /**
     * 合并同名路径信息
     *
     * @param preInfo 上一段路径信息
     * @param curInfo 当前路径信息
     * @return 合并后的路径信息
     */
    private static NavigationDetailInfoVo unionSamePath(NavigationDetailInfoVo preInfo, NavigationDetailInfoVo curInfo) {
        MultiLineString preMultiLineString = (MultiLineString) GeoUtils.buildGeometryFromGeoJsonStr(JSONObject.toJSONString(preInfo.getGeomJson()));
        MultiLineString curMultiLineString = (MultiLineString) GeoUtils.buildGeometryFromGeoJsonStr(JSONObject.toJSONString(curInfo.getGeomJson()));
        int preNum = preMultiLineString.getNumGeometries();
        int curNum = curMultiLineString.getNumGeometries();
        LineString[] allLineStrings = new LineString[preNum + curNum];
        for (int i = 0; i < preNum; i++) {
            allLineStrings[i] = (LineString) preMultiLineString.getGeometryN(i);
        }
        for (int i = 0; i < curNum; i++) {
            allLineStrings[preNum + i] = (LineString) preMultiLineString.getGeometryN(i);
        }
        MultiLineString unionMultiLineString = GeoUtils.buildMultiLineStringByLineStrings(allLineStrings);
        NavigationDetailInfoVo newInfo = new NavigationDetailInfoVo();
        newInfo.setAzimuth(preInfo.getAzimuth());
        newInfo.setLength(preInfo.getLength() + curInfo.getLength());
        newInfo.setPathName(preInfo.getPathName().length() > curInfo.getPathName().length() ? curInfo.getPathName() : preInfo.getPathName());
        newInfo.setSpeed(Math.max(preInfo.getSpeed(), preInfo.getSpeed()));
        newInfo.setGeomJson(JSONObject.parseObject(GeoUtils.getGeoJsonStrFromGeometry(unionMultiLineString)));
        newInfo.setPathType(curInfo.getPathType());
        newInfo.setRoadType(curInfo.getRoadType());
        return newInfo;
    }

    /**
     * 计算各类型路径长度
     *
     * @param edge   待计算的边
     * @param resMap 存放结果集
     */
    private static void calculatePathLength(CustomEdge edge, Map<String, Double> resMap) {
        NavigationConstants.PathTypeEnum edgeType = edge.getEdgeType();
        Double penalty = edge.getPathTopologyData().getPenalty();
        Double cost = edge.getPathTopologyData().getCost();
        if (null == edgeType || edgeType == NavigationConstants.PathTypeEnum.ROAD) {
            if (penalty < 1) {
                // 高速
                resMap.put("高速公路", resMap.getOrDefault("高速公路", 0.0) + cost);
            } else {
                // 普通道路
                resMap.put("普通道路", resMap.getOrDefault("普通道路", 0.0) + cost);
            }
        } else if (edgeType == NavigationConstants.PathTypeEnum.RAILWAY) {
            resMap.put("铁路", resMap.getOrDefault("铁路", 0.0) + cost);
        } else if (edgeType == NavigationConstants.PathTypeEnum.WATERWAY) {
            resMap.put("水路", resMap.getOrDefault("水路", 0.0) + cost);
        }
    }

    /**
     * 处理联运拓扑网络不互通，断连的情况
     *
     * @param lineStrings 初始路径
     * @return 合并成一整条的路径
     */
    private static LineString handleMixDisconnect(List<LineString> lineStrings) {
        List<LineString> extraLineStrings = new ArrayList<>();
        for (int i = 0; i < lineStrings.size() - 1; i++) {
            LineString curLineString = lineStrings.get(i);
            LineString nextLineString = lineStrings.get(i + 1);
            Point curEndPoint = curLineString.getEndPoint();
            Point nextStartPoint = nextLineString.getStartPoint();
            if (!StrUtil.equals(String.valueOf(curEndPoint.getX()), String.valueOf(nextStartPoint.getX())) || !StrUtil.equals(String.valueOf(curEndPoint.getY()), String.valueOf(nextStartPoint.getY()))) {
                extraLineStrings.add(GeoUtils.buildLineStringFromCoordinates(new double[][]{{curEndPoint.getX(), curEndPoint.getY()}, {nextStartPoint.getX(), nextStartPoint.getY()}}));
            }
        }
        lineStrings.addAll(extraLineStrings);
        return GeoUtils.unionLineString(lineStrings);
    }

    /**
     * 半径(米)转度数，用于画圆的时候，半径长度单位转换；
     * <b>仅粗略转换，适用于几公里的半径这种</b>
     *
     * @param meters   半径(米)
     * @param latitude 圆心纬度
     * @return 度数
     */
    private static double metersToDegrees(double meters, double latitude) {
        double meterPerDegreeLon = 111320 * Math.cos(Math.toRadians(latitude));
        double meterPerDegreeLat = 111320;

        double degreeLon = meters / meterPerDegreeLon;
        double degreeLat = meters / meterPerDegreeLat;

        return Math.max(degreeLon, degreeLat);
    }

    // 公路 ID-详情Map
    private static final Map<Integer, PathTopologyData> ROAD_MAP = new ConcurrentHashMap<>();
    // 铁路 ID-详情Map
    private static final Map<Integer, PathTopologyData> RAILWAY_MAP = new ConcurrentHashMap<>();
    // 水路 ID-详情Map
    private static final Map<Integer, PathTopologyData> WATERWAY_MAP = new ConcurrentHashMap<>();
    // 公路 拓扑节点IDs
    private static final List<Integer> ROAD_VERTEX_IDS = new CopyOnWriteArrayList<>();
    // 铁路 拓扑节点IDs
    private static final List<Integer> RAILWAY_VERTEX_IDS = new CopyOnWriteArrayList<>();
    // 水路 拓扑节点IDs
    private static final List<Integer> WATERWAY_VERTEX_IDS = new CopyOnWriteArrayList<>();
    // 公路路网-距离优先 拓扑图
    public static WeightedPseudograph<Integer, CustomEdge> ROAD_LENGTH_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
    // 公路路网-时间优先 拓扑图
    public static WeightedPseudograph<Integer, CustomEdge> ROAD_TIME_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
    // 公路路网-高速优先 拓扑图
    public static WeightedPseudograph<Integer, CustomEdge> ROAD_HIGHWAY_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
    // 铁路路网 拓扑图
    public static WeightedPseudograph<Integer, CustomEdge> RAILWAY_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
    // 水路路网 拓扑图
    public static WeightedPseudograph<Integer, CustomEdge> WATERWAY_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
    // 公水联运路网 拓扑图
    public static WeightedPseudograph<Integer, CustomEdge> ROAD_WATERWAY_MIXED_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
    // 公铁联运 拓扑图
    public static WeightedPseudograph<Integer, CustomEdge> ROAD_RAILWAY_MIXED_GRAPH = new WeightedPseudograph<>(CustomEdge.class);

    @PostConstruct
    public void initGraph() {
        LOGGER.info("[路径规划] ===> 开始初始化公路路网");
        this.initRoadGraph();
//        LOGGER.info("[路径规划] ===> 开始初始化铁路路网");
//        this.initRailwayGraph();
//        LOGGER.info("[路径规划] ===> 开始初始化水路路网");
//        this.initWaterwayGraph();
//        LOGGER.info("[路径规划] ===> 开始初始化公水联运路网");
//        this.initRoadWaterwayMixedGraph();
//        LOGGER.info("[路径规划] ===> 开始初始化公铁联运路网");
//        this.initRoadRailwayMixedGraph();
        LOGGER.info("[路径规划] ===> 路网初始化结束");
    }

    /**
     * 初始化公路路网拓扑图
     */
    private void initRoadGraph() {
        // 清空缓存
        ROAD_MAP.clear();
        ROAD_VERTEX_IDS.clear();
        ROAD_LENGTH_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
        ROAD_TIME_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
        ROAD_HIGHWAY_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
        // 查询路径详细信息并处理保存
        ROAD_VERTEX_IDS.addAll(navigationMapper.queryAllRoadVertex());
        List<RoadData> roadDataList = navigationMapper.queryAllRoad();
        roadDataList.parallelStream().forEach(data -> ROAD_MAP.put(data.getId(), this.buildPathInfo(data)));
        // 构建距离优先/时间有限/高速优先路网拓扑图
        ROAD_VERTEX_IDS.forEach(id -> {
            ROAD_LENGTH_GRAPH.addVertex(id);
            ROAD_TIME_GRAPH.addVertex(id);
            ROAD_HIGHWAY_GRAPH.addVertex(id);
        });
        ROAD_MAP.forEach((key, road) -> {
            CustomEdge lengthFirstEdge = ROAD_LENGTH_GRAPH.addEdge(road.getSource(), road.getTarget());
            lengthFirstEdge.setEdgeId(road.getId());
            lengthFirstEdge.setPathTopologyData(road);
            lengthFirstEdge.setEdgeType(NavigationConstants.PathTypeEnum.ROAD);
            lengthFirstEdge.setRoadTypeEnum(null != road.getPenalty() && road.getPenalty() < 1 ? NavigationConstants.RoadTypeEnum.HIGHWAY : NavigationConstants.RoadTypeEnum.NORMAL);
            ROAD_LENGTH_GRAPH.setEdgeWeight(lengthFirstEdge, road.getCost());

            CustomEdge timeFirstEdge = ROAD_TIME_GRAPH.addEdge(road.getSource(), road.getTarget());
            timeFirstEdge.setEdgeId(road.getId());
            timeFirstEdge.setPathTopologyData(road);
            timeFirstEdge.setEdgeType(NavigationConstants.PathTypeEnum.ROAD);
            timeFirstEdge.setRoadTypeEnum(null != road.getPenalty() && road.getPenalty() < 1 ? NavigationConstants.RoadTypeEnum.HIGHWAY : NavigationConstants.RoadTypeEnum.NORMAL);
            ROAD_TIME_GRAPH.setEdgeWeight(timeFirstEdge, road.getCost() / road.getSpeed());

            CustomEdge highwayFirstEdge = ROAD_HIGHWAY_GRAPH.addEdge(road.getSource(), road.getTarget());
            highwayFirstEdge.setEdgeId(road.getId());
            highwayFirstEdge.setPathTopologyData(road);
            highwayFirstEdge.setEdgeType(NavigationConstants.PathTypeEnum.ROAD);
            highwayFirstEdge.setRoadTypeEnum(null != road.getPenalty() && road.getPenalty() < 1 ? NavigationConstants.RoadTypeEnum.HIGHWAY : NavigationConstants.RoadTypeEnum.NORMAL);
            ROAD_HIGHWAY_GRAPH.setEdgeWeight(highwayFirstEdge, road.getCost() * road.getPenalty());
        });
    }

    /**
     * 初始化铁路路网拓扑图
     */
    private void initRailwayGraph() {
        // 清空缓存
        RAILWAY_MAP.clear();
        RAILWAY_VERTEX_IDS.clear();
        RAILWAY_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
        // 查询路径详细信息并处理保存
        RAILWAY_VERTEX_IDS.addAll(navigationMapper.queryAllRailwayVertex());
        List<RailwayData> railwayDataList = navigationMapper.queryAllRailway();
        railwayDataList.parallelStream().forEach(data -> RAILWAY_MAP.put(data.getId(), this.buildPathInfo(data)));
        // 构建铁路
        RAILWAY_VERTEX_IDS.forEach(id -> RAILWAY_GRAPH.addVertex(id));
        RAILWAY_MAP.forEach((id, railway) -> {
            CustomEdge railwayEdge = RAILWAY_GRAPH.addEdge(railway.getSource(), railway.getTarget());
            railwayEdge.setEdgeId(railway.getId());
            railwayEdge.setPathTopologyData(railway);
            railwayEdge.setEdgeType(NavigationConstants.PathTypeEnum.RAILWAY);
            RAILWAY_GRAPH.setEdgeWeight(railwayEdge, railway.getCost());
        });
    }

    /**
     * 初始化水路路网拓扑图
     */
    private void initWaterwayGraph() {
        // 清空缓存
        WATERWAY_MAP.clear();
        WATERWAY_VERTEX_IDS.clear();
        WATERWAY_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
        // 查询路径详细信息并处理保存
        WATERWAY_VERTEX_IDS.addAll(navigationMapper.queryAllWaterwayVertex());
        List<WaterwayData> waterwayDataList = navigationMapper.queryAllWaterway();
        waterwayDataList.parallelStream().forEach(data -> WATERWAY_MAP.put(data.getId(), this.buildPathInfo(data)));
        // 构建水路
        WATERWAY_VERTEX_IDS.forEach(id -> WATERWAY_GRAPH.addVertex(id));
        WATERWAY_MAP.forEach((id, waterway) -> {
            CustomEdge waterEdge = WATERWAY_GRAPH.addEdge(waterway.getSource(), waterway.getTarget());
            waterEdge.setEdgeId(waterway.getId());
            waterEdge.setPathTopologyData(waterway);
            waterEdge.setEdgeType(NavigationConstants.PathTypeEnum.WATERWAY);
            WATERWAY_GRAPH.setEdgeWeight(waterEdge, waterway.getCost());
        });
    }

    /**
     * 初始化公|水 混合路网---用于公水联运规划
     * 混合方法:
     * 1. 将水路拓扑点, 找近距离的公路拓扑点替代，超出一定距离则放弃加入规划
     * 2. 在构建边的时候,将水路拓扑边 放到公路拓扑点之间,生成混合拓扑图
     */
    private void initRoadWaterwayMixedGraph() {
        Map<Integer, Integer> waterwayRoadIdMap = navigationMapper.queryVertexMapping("sl")
                .stream().collect(Collectors.toMap(NavigationVertexMapping::getOtherVertexId, NavigationVertexMapping::getRoadVertexId));
        ROAD_WATERWAY_MIXED_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
        // 根据拓扑点和路径信息构建图
        ROAD_VERTEX_IDS.forEach(ROAD_WATERWAY_MIXED_GRAPH::addVertex);
        ROAD_MAP.forEach((key, road) -> {
            CustomEdge edge = ROAD_WATERWAY_MIXED_GRAPH.addEdge(road.getSource(), road.getTarget());
            edge.setEdgeId(road.getId());
            edge.setEdgeType(NavigationConstants.PathTypeEnum.ROAD);
            edge.setPathTopologyData(road);
            ROAD_WATERWAY_MIXED_GRAPH.setEdgeWeight(edge, road.getCost() / road.getSpeed());
        });
        WATERWAY_MAP.forEach((key, waterway) -> {
            Integer roadSource = waterwayRoadIdMap.get(waterway.getSource());
            Integer roadTarget = waterwayRoadIdMap.get(waterway.getTarget());
            if (null != roadSource && null != roadTarget) {
                CustomEdge edge = ROAD_WATERWAY_MIXED_GRAPH.addEdge(roadSource, roadTarget);
                edge.setEdgeId(waterway.getId());
                edge.setEdgeType(NavigationConstants.PathTypeEnum.WATERWAY);
                edge.setPathTopologyData(waterway);
                ROAD_WATERWAY_MIXED_GRAPH.setEdgeWeight(edge, (waterway.getCost() * 0.1) / waterway.getSpeed());
            }
        });
    }

    /**
     * 初始化公|铁 混合路网---用于公铁联运规划
     * 混合方法:
     * 1. 将铁路拓扑点, 找近距离的公路拓扑点替代，超出一定距离则放弃加入规划
     * 2. 在构建边的时候,将铁路拓扑边 放到公路拓扑点之间,生成混合拓扑图
     */
    private void initRoadRailwayMixedGraph() {
        Map<Integer, Integer> railwayRoadIdMap = navigationMapper.queryVertexMapping("tl")
                .stream().collect(Collectors.toMap(NavigationVertexMapping::getOtherVertexId, NavigationVertexMapping::getRoadVertexId));
        ROAD_RAILWAY_MIXED_GRAPH = new WeightedPseudograph<>(CustomEdge.class);
        // 根据拓扑点和路径信息构建图
        ROAD_VERTEX_IDS.forEach(ROAD_RAILWAY_MIXED_GRAPH::addVertex);
        ROAD_MAP.forEach((key, road) -> {
            CustomEdge edge = ROAD_RAILWAY_MIXED_GRAPH.addEdge(road.getSource(), road.getTarget());
            edge.setEdgeId(road.getId());
            edge.setEdgeType(NavigationConstants.PathTypeEnum.ROAD);
            edge.setPathTopologyData(road);
            ROAD_RAILWAY_MIXED_GRAPH.setEdgeWeight(edge, road.getCost() / road.getSpeed());
        });
        RAILWAY_MAP.forEach((key, waterway) -> {
            Integer roadSource = railwayRoadIdMap.get(waterway.getSource());
            Integer roadTarget = railwayRoadIdMap.get(waterway.getTarget());
            if (null != roadSource && null != roadTarget) {
                CustomEdge edge = ROAD_RAILWAY_MIXED_GRAPH.addEdge(roadSource, roadTarget);
                edge.setEdgeId(waterway.getId());
                edge.setEdgeType(NavigationConstants.PathTypeEnum.RAILWAY);
                edge.setPathTopologyData(waterway);
                ROAD_RAILWAY_MIXED_GRAPH.setEdgeWeight(edge, (waterway.getCost() * 0.1) / waterway.getSpeed());
            }
        });
    }

    /**
     * 根据原始数据构造部分道路信息(用时、正反向几何对象)
     *
     * @param data 原始拓扑数据 {@link PathTopologyData}
     * @return 修改后的拓扑数据
     */
    private PathTopologyData buildPathInfo(PathTopologyData data) {
        data.setMulLineGeometry((MultiLineString) GeoUtils.buildGeometryFromGeoJsonStr(data.getGeom()));
        data.setReverseMulLineGeometry(GeoUtils.reverseMultiLineString(data.getMulLineGeometry()));
        data.setRoadTime(data.getCost() / (data.getSpeed() * 1000 / 60));
        data.setLineGeometry(GeoUtils.unionMultiLineString(ListUtil.of(data.getMulLineGeometry())));
        data.setReverseLineGeometry(data.getLineGeometry().reverse());
        return data;
    }
}
